/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package clasificador.genetico.reglas;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import utils.Couple;
import utils.Weighted;
import clasificador.genetico.PoliticaCruce;

/**
 *
 * @author e235166
 */
public class CruceReglas2 implements PoliticaCruce<TGenotipo> {

    private double probCruce;
    private double decay;

    public CruceReglas2(double probCruce, double decay) {
        this.probCruce = probCruce;
        this.decay = decay;
    }

    @Override
    public List<TGenotipo> cruzar(Couple<Weighted<TGenotipo>> pareja) {
        TGenotipo genotipo1 = pareja.l().l();
        TGenotipo genotipo2 = pareja.r().l();

        Random r = new Random();

        int leng = r.nextInt(1)==0?genotipo1.size():genotipo2.size();
        
        TGenotipo subHijo1 = new TGenotipo(leng);
        TGenotipo subHijo2 = new TGenotipo(leng);

        double fit1 = pareja.l().r();
        double fit2 = pareja.r().r();
        double fitTot = fit1 + fit2;

        // Damos menor probabilidad de coger primero a las ultimas reglas de cada genotipo
        double sum1 = 0.0;
        double fact = 1.0;
        for (int i = 0; i < genotipo1.size(); i++) {
            sum1 += fact;
            fact *= decay;
        }
        fact = 1.0;
        double sum2 = 0.0;
        for (int i = 0; i < genotipo2.size(); i++) {
            sum2 += fact;
            fact *= decay;
        }

        ArrayList<Couple<Integer>> alreadyIn;
        int n1, n2;
        alreadyIn = new ArrayList<>(leng);
        n1 = 0; n2 = 0;
        for (int i = 0; i < leng; i++) {
            // Seleccionamos de que genotipo coger la regla proporcionalmente.
        	int iOrigen = (r.nextDouble() * fitTot < fit1) ? 1 : 2;
            TGenotipo origen = (iOrigen == 1) ? genotipo1 : genotipo2;            
            if(origen == genotipo1 && n1 >= genotipo1.size())
            {
            	iOrigen = 2;
            	origen = genotipo2;
            }
            if(origen == genotipo2 && n2 >= genotipo2.size())
            {
            	iOrigen = 1;
            	origen = genotipo1;	            
            }         
                        
            double sum = (iOrigen == 1) ? sum1 : sum2;
            
            int j;            
            do
            {
            	j = 0;
            	double d = r.nextDouble() * sum;
	            fact = 1.0;
	            double s = fact;
	            while (s < d) {
	                fact *= decay;
	                j++;
	                s += fact;	                              
	            }            
            } while (alreadyIn.contains(new Couple<>(iOrigen,j)));
            subHijo1.add(TGenotipo.copyRegla(origen.get(j)));
            alreadyIn.add(new Couple<>(iOrigen,j));
            if(iOrigen == 1) n1++;
            if(iOrigen == 2) n2++;
        }

        alreadyIn.clear();
        n1 = 0; n2 = 0;
        for (int i = 0; i < leng; i++) {
            // Seleccionamos de que genotipo coger la regla proporcionalmente.                        
            int iOrigen = (r.nextDouble() * fitTot < fit1) ? 1 : 2;
            TGenotipo origen = (iOrigen == 1) ? genotipo1 : genotipo2;
            if(origen == genotipo1 && n1 >= genotipo1.size())
            {
            	iOrigen = 2;
            	origen = genotipo2;
            }
            if(origen == genotipo2 && n2 >= genotipo2.size())
            {
            	iOrigen = 1;
            	origen = genotipo1;	            
            }                  
            
            double sum = (iOrigen == 1) ? sum1 : sum2;
            int j;            
            do
            {
            	j = 0;
	            double d = r.nextDouble() * sum;
	            fact = 1.0;
	            double s = fact;
	            while (s < d) {
	                fact *= decay;
	                j++;
	                s += fact;
	            }            
            } while (alreadyIn.contains(new Couple<>(iOrigen,j)));
            subHijo2.add(TGenotipo.copyRegla(origen.get(j)));
            alreadyIn.add(new Couple<>(iOrigen,j));
            if(iOrigen == 1) n1++;
            if(iOrigen == 2) n2++;
        }

        // Ahora, los dos subHijos tienen la misma longitud, los cruzamos regla a regla, atributo a atributo.
        int nAtributos = 0;
        if(leng > 0) nAtributos = subHijo1.get(0).l().length;
        for (int i = 0; i < leng; i++) {
            for (int j = 0; j < nAtributos; j++) {
                if (r.nextDouble() < probCruce) {
                    int swap = subHijo1.get(i).l()[j];
                    subHijo1.get(i).l()[j] = subHijo2.get(i).l()[j];
                    subHijo2.get(i).l()[j] = swap;
                }
            }
        }

        List<TGenotipo> res = new ArrayList<>(2);

        res.add(genotipo1);
        res.add(genotipo2);
        res.add(subHijo1);
        res.add(subHijo2);

        return res;
    }
}
